package pe.com.rimac.portal.service.facturacion.impl;

import java.io.ByteArrayInputStream;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.commons.lang.StringUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import pe.com.rimac.portal.common.config.ServiceConfig;
import pe.com.rimac.portal.common.exception.ServiceException;
import pe.com.rimac.portal.common.token.TokenGenerator;
import pe.com.rimac.portal.service.facturacion.FacturacionService;
import pe.com.rimac.portal.service.facturacion.criterio.CriterioBusqueda;
import pe.com.rimac.portal.service.facturacion.modelo.Documento;
import pe.com.rimac.portal.service.facturacion.request.FacturacionRequest;
import pe.com.rimac.portal.service.facturacion.trama.Header;
import pe.com.rimac.portal.service.facturacion.trama.Request;

import com.rimac.factelect.ejb.proxy.RetornoFacadeProxy;

/**
 * @author Synopsis S.A.
 */
public class FacturacionServiceImpl implements FacturacionService
{
	private static final String CODIGO_RESPUESTA = "CODIGO";
	private static final String MENSAJE_RESPUESTA = "MENSAJE";
	private static final String CODIGO_EXITO = "0";
	private static final String DOCUMENTO_SUNAT = "DocSunat";
	private static final String TIPO_DOCUMENTO_SUNAT = "TipoDocSun";
	private static final String SERIE_DOCUMENTO_SUNAT = "SerieSun";
	private static final String NUMERO_DOCUMENTO_SUNAT = "NumDocSun";
	private static final String CODIGO_ESTADO = "codestado";
	private static final String DESCRIPCION_ESTADO = "Descestado";
	private static final String CODIGO_PRODUCTO = "CodProd";
	private static final String DESCRIPCION_PRODUCTO = "descprod";
	private static final String NUMERO_POLIZA = "NumPol";
	private static final String NUMERO_FACTURA = "NumFact";
	private static final String TIPO_FACTURA = "TipoFact";
	private static final String CODIGO_MONEDA = "CodMoneda";
	private static final String INDICADOR_DOCUMENTO_MULTIPLE = "IndMultiDoc";
	private static final String INDICADOR_POLIZA_MULTIPLE = "IndMultiPol";
	private static final String NUMERO_BOLETA_COBRO = "NumRecCobro";
	private static final String IMPORTE = "Importe";
	private static final String CODIGO_EMPRESA = "CodCia";
	private static final String NOMBRE_EMPRESA = "NombreCia";
	private static final String NOMBRE_RESPONSABLE_PAGO = "NombreRespPago";
	private static final String NUMERO_ID_RESPONSABLE_PAGO = "NumIdRespPago";
	private static final String DIRECCION_RESPONSABLE_PAGO = "DireccionRespPago";
	private static final String FECHA_EMISION_DOCUMENTO = "FecemiDocSun";
	private ServiceConfig serviceConfig;
	private TokenGenerator tokenGenerator;
	private RetornoFacadeProxy port;

	public FacturacionServiceImpl(ServiceConfig serviceConfig, TokenGenerator tokenGenerator)
	{
		this.serviceConfig = serviceConfig;
		this.tokenGenerator = tokenGenerator;
		this.port = new RetornoFacadeProxy();
		this.port.setEndpoint(serviceConfig.getProperty("facturacion.ws.url"));
	}

	@Override
	public List<Documento> obtenerDocumentos(FacturacionRequest facturacionRequest, CriterioBusqueda criterioBusqueda)
			throws ServiceException
	{
		validarRequest(facturacionRequest, criterioBusqueda);
		Header header = prepararHeader(facturacionRequest, criterioBusqueda);
		Request request = prepararRequest(header, facturacionRequest, criterioBusqueda);
		String response = invoke(request);
		return procesarResponse(response);
	}

	private Header prepararHeader(FacturacionRequest facturacionRequest, CriterioBusqueda criterioBusqueda)
	{
		Header header = new Header();
		header.setIdTransaccion(serviceConfig.getProperty("ITRANSACCION"));
		header.setToken(obtenerToken(facturacionRequest, criterioBusqueda));
		header.setId(serviceConfig.getProperty("ID"));
		return header;
	}

	private Request prepararRequest(Header header, FacturacionRequest facturacionRequest,
			CriterioBusqueda criterioBusqueda)
	{
		Request request = new Request(header, facturacionRequest, criterioBusqueda);
		return request;
	}

	private String invoke(Request request)
	{
		try
		{
			return this.port.funcionesRemotasFactElec(request.build());
		}
		catch (RemoteException e)
		{
			throw new ServiceException("Ocurrio un error al consultar los documentos electronicos", e);
		}
	}

	private List<Documento> procesarResponse(String respuesta)
	{
		Document xml = obtenerXML(respuesta);
		return obtenerDocumentos(xml);
	}

	private List<Documento> obtenerDocumentos(Document xml)
	{
		List<Documento> documentos = new ArrayList<Documento>();
		xml.getDocumentElement().normalize();
		NodeList errorCodeNode = xml.getElementsByTagName(CODIGO_RESPUESTA);
		NodeList errorMessageNode = xml.getElementsByTagName(MENSAJE_RESPUESTA);
		Element errorCode = (Element) errorCodeNode.item(0);
		Element errorMessage = (Element) errorMessageNode.item(0);
		if (CODIGO_EXITO.equals(errorCode.getTextContent()))
		{
			NodeList nodes = xml.getElementsByTagName(DOCUMENTO_SUNAT);
			for (int i = 0; i < nodes.getLength(); i++)
			{
				Element element = (Element) nodes.item(i);
				if (element.getNodeType() == Node.ELEMENT_NODE)
				{
					NodeList childnodes = element.getChildNodes();
					Documento documento = new Documento();
					for (int j = 0; j < childnodes.getLength(); j++)
					{
						Node e = (Node) childnodes.item(j);
						if (e.getNodeType() == Node.ELEMENT_NODE)
						{
							Element elem = (Element) e;
							if (TIPO_DOCUMENTO_SUNAT.equals(elem.getNodeName()))
							{
								documento.setTipo(elem.getTextContent().trim());
							}
							else if (SERIE_DOCUMENTO_SUNAT.equals(elem.getNodeName()))
							{
								documento.setSerie(elem.getTextContent().trim());
							}
							else if (NUMERO_DOCUMENTO_SUNAT.equals(elem.getNodeName()))
							{
								documento.setNumero(elem.getTextContent().trim());
							}
							else if (CODIGO_ESTADO.equals(elem.getNodeName()))
							{
								documento.setCodigoEstado(elem.getTextContent().trim());
							}
							else if (DESCRIPCION_ESTADO.equals(elem.getNodeName()))
							{
								documento.setEstado(elem.getTextContent().trim());
							}
							else if (CODIGO_PRODUCTO.equals(elem.getNodeName()))
							{
								documento.setCodigoProducto(elem.getTextContent().trim());
							}
							else if (DESCRIPCION_PRODUCTO.equals(elem.getNodeName()))
							{
								documento.setProducto(elem.getTextContent().trim());
							}
							else if (NUMERO_POLIZA.equals(elem.getNodeName()))
							{
								documento.setNumeroPoliza(elem.getTextContent().trim());
							}
							else if (NUMERO_FACTURA.equals(elem.getNodeName()))
							{
								documento.setNumeroFactura(elem.getTextContent().trim());
							}
							else if (TIPO_FACTURA.equals(elem.getNodeName()))
							{
								documento.setTipoFactura(elem.getTextContent().trim());
							}
							else if (CODIGO_MONEDA.equals(elem.getNodeName()))
							{
								documento.setCodigoMoneda(elem.getTextContent().trim());
							}
							else if (INDICADOR_DOCUMENTO_MULTIPLE.equals(elem.getNodeName()))
							{
								documento.setIndicadorMultiDocumento(elem.getTextContent().trim());
							}
							else if (INDICADOR_POLIZA_MULTIPLE.equals(elem.getNodeName()))
							{
								documento.setIndicadorMultiPoliza(elem.getTextContent().trim());
							}
							else if (NUMERO_BOLETA_COBRO.equals(elem.getNodeName()))
							{
								documento.setNumeroBoletaCobro(elem.getTextContent().trim());
							}
							else if (IMPORTE.equals(elem.getNodeName()))
							{
								documento.setImporte(elem.getTextContent().trim());
							}
							else if (CODIGO_EMPRESA.equals(elem.getNodeName()))
							{
								documento.setCodigoEmpresa(elem.getTextContent().trim());
							}
							else if (NOMBRE_EMPRESA.equals(elem.getNodeName()))
							{
								documento.setNombreEmpresa(elem.getTextContent().trim());
							}
							else if (NOMBRE_RESPONSABLE_PAGO.equals(elem.getNodeName()))
							{
								documento.setNombreResponsablePago(elem.getTextContent().trim());
							}
							else if (NUMERO_ID_RESPONSABLE_PAGO.equals(elem.getNodeName()))
							{
								documento.setNumIdResponsablePago(elem.getTextContent().trim());
							}
							else if (DIRECCION_RESPONSABLE_PAGO.equals(elem.getNodeName()))
							{
								documento.setDireccionReponsablePago(elem.getTextContent().trim());
							}
							else if (FECHA_EMISION_DOCUMENTO.equals(elem.getNodeName()))
							{
								documento.setFechaEmisionDocumento(elem.getTextContent().trim());
							}
						}
					}
					documentos.add(documento);
				}
			}
			return documentos;
		}
		throw new ServiceException("Ocurrio un error al momento de consultar los documentos electronicos. Error: '"
				+ errorCode.getTextContent() + "' - '" + errorMessage.getTextContent() + "'");
	}

	private Document obtenerXML(String xmlAsString)
	{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setNamespaceAware(false);
		DocumentBuilder builder;
		Document xml = null;
		// Se cambia el encoding antes
		xmlAsString = xmlAsString.replace("UTF-8", "ISO-8859-1");
		try
		{
			builder = factory.newDocumentBuilder();
			xml = builder.parse(new ByteArrayInputStream(xmlAsString.getBytes()));
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return xml;
	}

	private void validarRequest(FacturacionRequest request, CriterioBusqueda criterioBusqueda)
	{
		if (StringUtils.isNotBlank(request.getTipoDocumento()) || StringUtils.isNotBlank(request.getNumeroDocumento()))
		{
			if (CriterioBusqueda.POR_FECHAS.equals(criterioBusqueda))
			{
				if (StringUtils.isBlank(request.getFechaInicio()) || StringUtils.isBlank(request.getFechaFin()))
				{
					throw new RuntimeException("Los datos para el criterio de busqueda seleccionado no son validos.");
				}
			}
			else if (CriterioBusqueda.POR_NUMERO_DOCUMENTO_SUNAT.equals(criterioBusqueda))
			{
				if (StringUtils.isBlank(request.getNumeroDocumentoSUNAT())
						|| StringUtils.isBlank(request.getTipoDocumentoSUNAT())
						|| StringUtils.isBlank(request.getSerieDocumentoSUNAT()))
				{
					throw new RuntimeException("Los datos para el criterio de busqueda seleccionado no son validos.");
				}
			}
			else if (CriterioBusqueda.POR_PRODUCTO_Y_POLIZA.equals(criterioBusqueda))
			{
				if (StringUtils.isBlank(request.getCodigoProducto()) || StringUtils.isBlank(request.getPoliza())
						|| StringUtils.isBlank(request.getProductoFechaInicio())
						|| StringUtils.isBlank(request.getProductoFechaFin()))
				{
					throw new RuntimeException("Los datos para el criterio de busqueda seleccionado no son validos.");
				}
			}
			else if (CriterioBusqueda.POR_NUMERO_DOCUMENTO_COBRO.equals(criterioBusqueda))
			{
				if (StringUtils.isBlank(request.getDocumentoCobro()))
				{
					throw new RuntimeException("Los datos para el criterio de busqueda seleccionado no son validos.");
				}
			}
		}
	}

	/**
	 * Obtiene el token generado en base a los par&aacute;metros de b&uacute;squeda ingresados.
	 */
	private String obtenerToken(FacturacionRequest request, CriterioBusqueda criterioBusqueda)
	{
		StringBuilder params = new StringBuilder();
		params.append(criterioBusqueda.getValue());
		params.append(request.getEmpresa());
		params.append(request.getTipoDocumento());
		params.append(request.getNumeroDocumento());
		if (CriterioBusqueda.POR_FECHAS.equals(criterioBusqueda))
		{
			params.append(request.getFechaInicio());
			params.append(request.getFechaFin());
		}
		else if (CriterioBusqueda.POR_NUMERO_DOCUMENTO_SUNAT.equals(criterioBusqueda))
		{
			params.append(request.getTipoDocumentoSUNAT());
			params.append(request.getSerieDocumentoSUNAT());
			params.append(request.getNumeroDocumentoSUNAT());
		}
		else if (CriterioBusqueda.POR_PRODUCTO_Y_POLIZA.equals(criterioBusqueda))
		{
			params.append(request.getProductoFechaInicio());
			params.append(request.getProductoFechaFin());
			params.append(request.getCodigoProducto());
			params.append(request.getPoliza());
		}
		else if (CriterioBusqueda.POR_NUMERO_DOCUMENTO_COBRO.equals(criterioBusqueda))
		{
			params.append(request.getDocumentoCobro());
		}
		return tokenGenerator.generate(serviceConfig.getProperty("ITRANSACCION"), serviceConfig.getProperty("ID"),
				serviceConfig.getProperty("KEYWORD"), params.toString());
	}
}